import { useSettingStore } from '@/store/bpmnProcess/settingStore'
import { useModelerStore } from '@/store/bpmnProcess/modelerStore'
import { getBusinessObject } from 'bpmn-js/lib/util/ModelUtil'
import { getServiceTaskLikeBusinessObject, isServiceTaskLike, isDmnCapable, isExternalCapable, getImplementationType, getConnectors, getConnector } from './bpmnPropertyUtils/eventDefinition'
import { createModdleElement } from './bpmnPropertyUtils/extensionElements'
import { sortBy, without } from 'min-dash'

export const isImplementSupported = (element) => {
  return getServiceTaskLikeBusinessObject(element)
}

export const getImplementData = (element) => {
  const prefix = useSettingStore().editor.processEngine
  const businessObject = getServiceTaskLikeBusinessObject(element)
  const connector = getConnector(element)
  const dmnBusinessObject = getBusinessObject(element)
  const res = {
    type: getImplementationType(element) || ''
  }
  if (res.type === 'class') {
    res['class'] = businessObject.get(`${prefix}:class`) || ''
  } else if (res.type === 'expression') {
    res['expression'] = businessObject.get(`${prefix}:expression`) || ''
    res['resultVariable'] = businessObject.get(`${prefix}:resultVariable`) || ''
  } else if (res.type === 'delegateExpression') {
    res['delegateExpression'] = businessObject.get(`${prefix}:delegateExpression`) || ''
  } else if (res.type === 'dmn') {
    res['decisionRef'] = dmnBusinessObject.get(`${prefix}:decisionRef`) || ''
    res['decisionRefBinding'] = dmnBusinessObject.get(`${prefix}:decisionRefBinding`) || 'latest'
    if (res['decisionRefBinding'] === 'version') {
      res['decisionRefVersion'] = dmnBusinessObject.get(`${prefix}:decisionRefVersion`) || ''
    } else if (res['decisionRefBinding'] === 'versionTag') {
      res['decisionRefVersionTag'] = dmnBusinessObject.get(`${prefix}:decisionRefVersionTag`) || ''
    }
    res['decisionRefTenantId'] = dmnBusinessObject.get(`${prefix}:decisionRefTenantId`) || ''
    res['decisionRefResultVariable'] = dmnBusinessObject.get(`${prefix}:resultVariable`) || ''
    if (res['decisionRefResultVariable'] !== '') {
      res['mapDecisionResult'] = dmnBusinessObject.get(`${prefix}:mapDecisionResult`) || 'resultList'
    }
  } else if (res.type === 'external') {
    res['topic'] = businessObject.get(`${prefix}:topic`) || ''
  } else if (res.type === 'connector') {
    res['connectorId'] = connector.get(`${prefix}:connectorId`) || ''
  }
  return res
}

export const setValue = (element, type, value) => {
  const prefix = useSettingStore().editor.processEngine
  const modeling = useModelerStore().getModeling
  const businessObject = getServiceTaskLikeBusinessObject(element)
  const connector = getConnector(element)
  const dmnBusinessObject = getBusinessObject(element)
  if (type === 'type') {
    const oldType = getImplementationType(element)
    let updatedProperties = {
      [`${prefix}:class`]: undefined,
      [`${prefix}:expression`]: undefined,
      [`${prefix}:delegateExpression`]: undefined,
      [`${prefix}:resultVariable`]: undefined
    }
    let extensionElements = businessObject.get('extensionElements')

    // (1) class, expression, delegateExpression
    if (isDelegateType(value)) {
      updatedProperties = {
        ...updatedProperties,
        [value]: isDelegateType(oldType) ? businessObject.get(`${prefix}:${oldType}`) : ''
      }
    }

    // (2) dmn
    if (isDmnCapable(businessObject)) {
      updatedProperties = {
        ...updatedProperties,
        [`${prefix}:decisionRef`]: undefined,
        [`${prefix}:decisionRefBinding`]: 'latest',
        [`${prefix}:decisionRefVersion`]: undefined,
        [`${prefix}:mapDecisionResult`]: 'resultList',
        [`${prefix}:decisionRefTenantId`]: undefined
      }
      if (value === 'dmn') {
        updatedProperties = {
          ...updatedProperties,
          [`${prefix}:decisionRef`]: ''
        }
      }
    }

    // (3) external
    // Note: error event definition elements got cleaned up in modeling behavior
    // cf. https://github.com/camunda/camunda-bpmn-js/blob/main/lib/camunda-platform/features/modeling/behavior/DeleteErrorEventDefinitionBehavior.js
    if (isExternalCapable(businessObject)) {
      updatedProperties = {
        ...updatedProperties,
        [`${prefix}:type`]: undefined,
        [`${prefix}:topic`]: undefined
      }
      if (value === 'external') {
        updatedProperties = {
          ...updatedProperties,
          [`${prefix}:type`]: 'external',
          [`${prefix}:topic`]: ''
        }
      }
    }

    // (4) connector
    if (isServiceTaskLike(businessObject)) {
      // (4.1) remove all connectors on type change
      const connectors = getConnectors(businessObject)
      if (connectors.length) {
        modeling.updateModdleProperties(element, extensionElements, { values: without(extensionElements.get('values'), (value) => connectors.includes(value)) })
      }

      // (4.2) create connector
      if (value === 'connector') {
        // ensure extension elements
        if (!extensionElements) {
          extensionElements = createModdleElement('bpmn:ExtensionElements', { values: [] }, businessObject)
          modeling.updateModdleProperties(element, businessObject, { extensionElements })
        }
        const connector = createModdleElement(`${prefix}:Connector`, {}, extensionElements)
        modeling.updateModdleProperties(element, extensionElements, { values: [...extensionElements.get('values'), connector] })
      }

      // (5) collect all property updates
      modeling.updateModdleProperties(element, businessObject, updatedProperties)
    }
  } else if (type === 'connectorId') {
    modeling.updateModdleProperties(element, connector, { [`${prefix}:${type}`]: value })
  } else if (type === 'decisionRef' || type === 'decisionRefVersion' || type === 'decisionRefVersionTag' || type === 'decisionRefTenantId' || type === 'mapDecisionResult') {
    modeling.updateModdleProperties(element, dmnBusinessObject, { [`${prefix}:${type}`]: value })
  } else if (type === 'decisionRefResultVariable') {
    modeling.updateModdleProperties(element, dmnBusinessObject, { [`${prefix}:resultVariable`]: value })
  } else if (type === 'decisionRefBinding') {
    const updatedProperties = {
      [`${prefix}:decisionRefVersion`]: undefined,
      [`${prefix}:decisionRefVersionTag`]: undefined,
      [`${prefix}:decisionRefBinding`]: value
    }
    modeling.updateModdleProperties(element, dmnBusinessObject, updatedProperties)
  } else {
    modeling.updateModdleProperties(element, businessObject, { [`${prefix}:${type}`]: value })
  }
}

export const getImplementationTypeOptions = (element) => {
  const businessObject = getServiceTaskLikeBusinessObject(element)
  const options = [
    {
      value: 'class',
      label: 'Java 类'
    },
    {
      value: 'expression',
      label: '表达式'
    },
    {
      value: 'delegateExpression',
      label: '代理表达式'
    }
  ]
  if (isDmnCapable(businessObject)) {
    options.push({
      value: 'dmn',
      label: 'DMN'
    })
  }
  if (isExternalCapable(businessObject)) {
    options.push({
      value: 'external',
      label: '外部'
    })
  }
  if (isServiceTaskLike(businessObject)) {
    options.push({
      value: 'connector',
      label: '连接器'
    })
  }
  return sortByPriority(options)
}

function sortByPriority(options) {
  const priorities = {
    class: 1,
    expression: 2,
    delegateExpression: 3,
    dmn: 0,
    external: 4,
    connector: 5
  }
  return sortBy(options, (o: any) => priorities[o.value])
}

export const getBindingTypeOptions = () => {
  const options = [
    { value: 'deployment', label: '部署版本' },
    { value: 'latest', label: '最新版本' },
    { value: 'version', label: '指定版本' },
    { value: 'versionTag', label: '版本标签' }
  ]
  return options
}

export const getMapDecisionResultOptions = () => {
  const options = [
    { value: 'collectEntries', label: '收集条目 （列表<对象>）' },
    { value: 'resultList', label: '结果列表（列表<映射<字符串，对象>>）' },
    { value: 'singleEntry', label: '单个条目（类型值）' },
    { value: 'singleResult', label: '单一结果（映射<字符串，对象>）' }
  ]
  return options
}

function isDelegateType(type) {
  return ['class', 'expression', 'delegateExpression'].includes(type)
}

export function getResultVariable(element) {
  const prefix = useSettingStore().editor.processEngine
  const businessObject = getBusinessObject(element)
  return businessObject.get(`${prefix}:resultVariable`)
}
